package top.gytf.unifiedcall.common.contract;

import cn.hutool.core.collection.CollUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import top.gytf.distools.lifecycle.AbstractLifecycle;
import top.gytf.distools.lifecycle.Lifecycle;
import top.gytf.unifiedcall.model.contract.ApiContract;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 组合多个ApiContractReader
 */
@RequiredArgsConstructor
public class CompositeApiContractReader extends AbstractLifecycle implements ApiContractReader {
    private final List<ApiContractReader> readers;
    private final Map<Method, List<ApiContractReader>> mapping = new ConcurrentHashMap<>();

    @Override
    protected void doInitialize() {
        readers.forEach(Lifecycle::initialize);
    }

    @Override
    protected void doDestroy() {
        readers.forEach(Lifecycle::destroy);
    }

    @Override
    public boolean isSupported(Method apiMethod) {
        assertReady();

        return CollUtil.isNotEmpty(selectReaders(apiMethod));
    }

    @Override
    public List<ApiContract> read(Method apiMethod) {
        assertReady();

        List<ApiContractReader> candidates = selectReaders(apiMethod);
        if (!CollectionUtils.isEmpty(candidates)) {
            List<ApiContract> result = new ArrayList<>();
            for (ApiContractReader candidate : candidates) {
                List<ApiContract> apiContracts = candidate.read(apiMethod);
                CollUtil.addAll(result, apiContracts);
            }
            return result;
        }
        return Collections.emptyList();
    }

    /**
     * 选择支持API方法的ApiContractReader
     *
     * @param apiMethod api方法
     *
     * @return 选择到的Reader，如果没有找到，返回 {@code null}
     */
    @Nullable
    protected List<ApiContractReader> selectReaders(Method apiMethod) {
        return mapping.computeIfAbsent(apiMethod, method -> {
            List<ApiContractReader> result = new ArrayList<>();
            for (ApiContractReader delegate : readers) {
                if (delegate.isSupported(method)) {
                    result.add(delegate);
                }
            }
            return result;
        });
    }
}
